// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#include <pollux/common/process/process_base.h>
#include <pollux/flag_definitions/flags.h>
#include <limits.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

#include <melon/cpu_id.h>
#include <melon/file_util.h>
#include <melon/string.h>
#include <turbo/flags/flag.h>

constexpr const char *kProcSelfCmdline = "/proc/self/cmdline";

namespace kumo {
    namespace pollux {
        namespace process {
            /**
             * Current executable's name.
             */
            std::string getAppName() {
                const char *result = getenv("_");
                if (result) {
                    return result;
                }

                // if we're running under gtest, getenv will return null
                std::string appName;
                if (melon::readFile(kProcSelfCmdline, appName)) {
                    auto pos = appName.find('\0');
                    if (pos != std::string::npos) {
                        appName = appName.substr(0, pos);
                    }

                    return appName;
                }

                return "";
            }

            /**
             * This machine's name.
             */
            std::string getHostName() {
                char hostbuf[_POSIX_HOST_NAME_MAX + 1] = {0};
                if (gethostname(hostbuf, _POSIX_HOST_NAME_MAX + 1) < 0) {
                    return "";
                } else {
                    // When the host name is precisely HOST_NAME_MAX bytes long, gethostname
                    // returns 0 even though the result is not NUL-terminated. Manually NUL-
                    // terminate to handle that case.
                    hostbuf[_POSIX_HOST_NAME_MAX] = '\0';
                    return hostbuf;
                }
            }

            /**
             * Process identifier.
             */
            pid_t getProcessId() {
                return getpid();
            }

            /**
             * Current thread's identifier.
             */
            pthread_t getThreadId() {
                return pthread_self();
            }

            /**
             * Get current working directory.
             */
            std::string getCurrentDirectory() {
                char buf[PATH_MAX];
                return getcwd(buf, PATH_MAX);
            }

            uint64_t threadCpuNanos() {
                timespec ts;
                clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
                return ts.tv_sec * 1'000'000'000 + ts.tv_nsec;
            }

            namespace {
                bool bmi2CpuFlag = melon::CpuId().bmi2();
                bool avx2CpuFlag = melon::CpuId().avx2();
            } // namespace

            bool hasAvx2() {
#ifdef __AVX2__
                return avx2CpuFlag && turbo::get_flag(FLAGS_avx2);
#else
  return false;
#endif
            }

            bool hasBmi2() {
#ifdef __BMI2__
                return bmi2CpuFlag && turbo::get_flag(FLAGS_bmi2);
#else
  return false;
#endif
            }
        } // namespace process
    } // namespace pollux
} // namespace kumo
